package net.messagenet.mazor.infra.persistence;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import net.messagenet.mazor.DO.CustomerDO;
import net.messagenet.mazor.DO.DpaDO;
import net.messagenet.mazor.DO.DpaProductDO;
import net.messagenet.mazor.DO.PosDO;
import net.messagenet.mazor.DO.PosProductDO;
import net.messagenet.mazor.DO.ProductDO;
import net.messagenet.mazor.exception.MZRApplicativeException;
import net.messagenet.mazor.infra.cache.CacheManager;
import net.messagenet.mazor.infra.persistence.entities.Customer;
import net.messagenet.mazor.infra.persistence.entities.Dpa;
import net.messagenet.mazor.infra.persistence.entities.DpaProduct;
import net.messagenet.mazor.infra.persistence.entities.Pos;
import net.messagenet.mazor.infra.persistence.entities.PosProduct;
import net.messagenet.mazor.infra.persistence.entities.Product;

import org.apache.log4j.Logger;


/**
 * This class is the standard implementation of the persistance manger for the mazor system
 * it manages all the operations on the database
 *
 * @author Idan
 */


public class PersistenceManagerImpl implements PersistenceManager {

	private Logger logger =  Logger.getLogger(this.getClass());
	private DataObjectConverter dataObjectConverter= new DataObjectConverter();
	private CacheManager cacheManager;
	
	private EntityManager em;

	public PersistenceManagerImpl() {
		logger.debug("PersistenceManagerImpl built");
		
	}
	

	public void setEntityManager(EntityManager em){
		this.em=em;
	}
	
	private Customer createIfNotExsits(CustomerDO customerDO){
		if (!customerDO.getName().equals("")){
			Customer customerEnt;
			if (customerDO.getId() != 0) {
				customerEnt = em.find(Customer.class, new Integer(customerDO
						.getId()));
				
			} else {
				customerEnt = dataObjectConverter.convert(customerDO);
				em.persist(customerEnt);
				customerDO.setId(customerEnt.getId());
				cacheManager.addCustomer(customerDO);
			}
			return customerEnt;
		}else{
			return null;
		}

	}
	private Product createIfNotExsits(ProductDO prdDO){
		if (!prdDO.getProductName().equals("")){
			Product prd;
			if (prdDO.getId()!=0){
				prd= em.find(Product.class, new Integer(prdDO.getId()));
			}else{
				prd = new Product();
				prd.setProductName(prdDO.getProductName());
				prd.setDialogicItemKey(prdDO.getDialogicItemKey());
				em.persist(prd);
				prdDO.setId(prd.getId());
				cacheManager.addProduct(prdDO);
			}
			return prd;
		}else
			return null;
	}

	@Override
	public void saveDPARequest(DpaDO dpa) {
		//check if this is a new dpa
		if (dpa.getId()==0){
			saveNewDPA(dpa,false);
		}else{
			updateDPA(dpa,false);
		}
	}

	

	@Override
	public void saveDPADraft(DpaDO dpa) {
		//check if this is a new dpa
		if (dpa.getId()==0){
			saveNewDPA(dpa,true);
		}else{
			updateDPA(dpa,true);
		}
		
	}
	
	private void saveNewDPA(DpaDO dpa,boolean isDraft) {
		Dpa dpaEnt = dataObjectConverter.convert(dpa);
	
		Customer customerEnt = createIfNotExsits(dpa.getCustomer());


		dpaEnt.setCustomer(customerEnt);
		dpaEnt.setDraft(isDraft);
		dpaEnt.setApproved(true);
		dpaEnt.setClosed(false);
		em.persist(dpaEnt);
		LinkedList<DpaProduct> lis = new LinkedList<DpaProduct>();
		
		List<DpaProductDO> productDOs = dpa.getDpaProducts();
		for (DpaProductDO dpaProductDO : productDOs) {
			Product prd =createIfNotExsits(dpaProductDO.getProduct());

			DpaProduct dpaPrd= new DpaProduct();
			
			dpaPrd.setProduct(prd);
			dataObjectConverter.fillDpaProductData(dpaPrd, dpaProductDO);

			dpaPrd.setDpa(dpaEnt);
			lis.add(dpaPrd);
			em.persist(dpaPrd);
			
		}
		
		dpaEnt.setDpaProducts(lis);
		//setting the new id of the dpa
		dpa.setId(dpaEnt.getId());
	}
	private void updateDPA(DpaDO dpa, boolean isDraft) {
		Dpa dpaEnt = em.find(Dpa.class, new Integer(dpa.getId()));
		
		Customer customerEnt = createIfNotExsits(dpa.getCustomer());


		dpaEnt.setCustomer(customerEnt);
		dpaEnt.setDraft(isDraft);
		dpaEnt.setApproved(true);
		dpaEnt.setClosed(false);
		dataObjectConverter.fillDpaData(dpaEnt, dpa);
		em.persist(dpaEnt);
		LinkedList<DpaProduct> lis = new LinkedList<DpaProduct>();
		
		List<DpaProductDO> productDOs = dpa.getDpaProducts();
		for (DpaProductDO dpaProductDO : productDOs) {
			if (dpaProductDO.getId()==0){
				Product prd =createIfNotExsits(dpaProductDO.getProduct());

				DpaProduct dpaPrd= new DpaProduct();
				
				dpaPrd.setProduct(prd);
				dataObjectConverter.fillDpaProductData(dpaPrd, dpaProductDO);

				dpaPrd.setDpa(dpaEnt);
				lis.add(dpaPrd);
				em.persist(dpaPrd);
			}else{
				DpaProduct dpaPrd = em.find(DpaProduct.class, new Integer(dpaProductDO.getId()));
				
				Product prd =createIfNotExsits(dpaProductDO.getProduct());

				
				dpaPrd.setProduct(prd);
				dataObjectConverter.fillDpaProductData(dpaPrd, dpaProductDO);

				dpaPrd.setDpa(dpaEnt);
				lis.add(dpaPrd);
				em.persist(dpaPrd);
			}
	
		}
		
		dpaEnt.setDpaProducts(lis);
		//setting the new id of the dpa
		dpa.setId(dpaEnt.getId());
		
	}



	@Override
	public void fillCache() {
		cacheManager.clear();
		Query q = em.createQuery(HQLQueries.GET_ALL_CUSTOMERS);
		List l = q.getResultList();

		List<CustomerDO> customerDOs = new LinkedList<CustomerDO>();
		for (Iterator iterator = l.iterator(); iterator.hasNext();) {
			Customer c = (Customer) iterator.next();
			customerDOs.add(dataObjectConverter.convert(c));

		}
		cacheManager.setCustomerList(customerDOs);
		// filiing products
		q = em.createQuery(HQLQueries.GET_ALL_PRODUCT);
		l = q.getResultList();

		List<ProductDO> productDOs = new LinkedList<ProductDO>();
		for (Iterator iterator = l.iterator(); iterator.hasNext();) {
			Product c = (Product) iterator.next();
			productDOs.add(dataObjectConverter.convert(c));

		}
		cacheManager.setCustomerList(customerDOs);
		cacheManager.setProductlist(productDOs);

	}





	@Override
	public void setCacheManager(CacheManager cacheManager) {
		this.cacheManager = cacheManager;

	}


	@Override
	public Map<String, DpaDO> getAllApprovedDPAs() {
		logger.debug("getting all the dpas from the DB");
		Query q = em.createQuery(HQLQueries.GET_ALL_NOT_EXPIRED_DPAS);
		List<Dpa> l = q.getResultList();

		Map<String, DpaDO> dpasDO = new HashMap<String, DpaDO>();
		for (Dpa dpa : l) {
			//not calling full convert becasue of the condition on the quntity
			DpaDO dpaDO = dataObjectConverter.convert(dpa);
			dpaDO.setCustomer(dataObjectConverter.convert(dpa.getCustomer()));

			//checking if the the products from the dpa has more units to use
			List<DpaProductDO> dpasprodDO = new LinkedList<DpaProductDO>();
			for (DpaProduct product: dpa.getDpaProducts()) {
				
				if (product.getQuantityReported()<product.getQuantity()){
					dpasprodDO.add(dataObjectConverter.convert(product));
				}
			}
			if (dpasprodDO.size()!=0){
				//the dpa is not empty and has product to use
				dpaDO.setDpaProducts(dpasprodDO);
				dpasDO.put(dpaDO.getDpaId(), dpaDO);
				
			}
		}
		return dpasDO;
	}




	@Override
	public void updateDPARequest(String dpaId, int dpaRequestId,
			Date experationDate, LinkedList<Double> approvedPrices)
			throws MZRApplicativeException {
		Dpa dpa = em.find(Dpa.class, new Integer(dpaRequestId));
		if (dpa!=null){
			if (dpaId!=null && !dpaId.equals("") &&!dpaId.equals(" ")){
				dpa.setDpaId(dpaId);
				dpa.setExpirationDate(experationDate);
				dpa.setApproved(true);
				em.persist(dpa);
				
				Iterator<Double> priceIte = approvedPrices.iterator();
				Iterator<DpaProduct> prodIte = dpa.getDpaProducts().iterator();
				
				while (prodIte.hasNext() && priceIte.hasNext()) {
					DpaProduct dpaProduct = (DpaProduct) prodIte.next();
					Double price = (Double) priceIte.next();
					dpaProduct.setApprovedPrice(price);
					em.persist(dpaProduct);
				}
			}else{
				dpa.setApproved(false);
				em.persist(dpa);
			}
			
		}else
			throw new MZRApplicativeException("could not find the dpa request id");
		
	}


	@Override
	public void savePOSrequest(PosDO pos) {
		//check if this is a new pos
		if (pos.getId()==0){
			createNewPOS(pos,false);
		}else{
			updatePOS(pos,false);
		}
	}

	@Override
	public void savePOSDraft(PosDO pos) {
		//check if this is a new pos
		if (pos.getId()==0){
			createNewPOS(pos,true);
		}else{
			updatePOS(pos,true);
		}
	}
	private void createNewPOS(PosDO pos , boolean isDraft){
		Pos posReport=dataObjectConverter.convert(pos);
		posReport.setDraft(isDraft);
		LinkedList<PosProduct> products = new LinkedList<PosProduct>();
		em.persist(posReport);
		for (PosProductDO poProductDO : pos.getPosProducts()) {
			PosProduct posProduct = dataObjectConverter.convert(poProductDO);
			Customer c = createIfNotExsits(poProductDO.getCustomer());
			Product p  = createIfNotExsits(poProductDO.getProduct());
			if (poProductDO.getDpaProduct()!=null){
				DpaProduct dpaProduct=em.find(DpaProduct.class, new Integer(poProductDO.getDpaProduct().getId()));
				posProduct.setDpaProduct(dpaProduct);
			}
			posProduct.setCustomer(c);
			posProduct.setProduct(p);
			posProduct.setPos(posReport);
			em.persist(posProduct);
			
		}
		posReport.setPosProducts(products);
	}
	private void updatePOS(PosDO pos , boolean isDraft){
		Pos posReport=em.find(Pos.class, new Integer(pos.getId()));
		dataObjectConverter.fillPOSData(posReport,pos);
		posReport.setDraft(isDraft);
		LinkedList<PosProduct> products = new LinkedList<PosProduct>();
		
		em.persist(posReport);
		for (PosProductDO poProductDO : pos.getPosProducts()) {
			PosProduct posProduct;
			if (poProductDO.getId()==0){
				 posProduct = dataObjectConverter.convert(poProductDO);

			}else{
				posProduct = em.find(PosProduct.class, new Integer(poProductDO.getId()));
				dataObjectConverter.fillDpaProductData(posProduct,poProductDO);

			}
			Customer c = createIfNotExsits(poProductDO.getCustomer());
			Product p  = createIfNotExsits(poProductDO.getProduct());
			if (poProductDO.getDpaProduct()!=null){
				DpaProduct dpaProduct=em.find(DpaProduct.class, new Integer(poProductDO.getDpaProduct().getId()));
				posProduct.setDpaProduct(dpaProduct);
			}
			posProduct.setCustomer(c);
			posProduct.setProduct(p);
			posProduct.setPos(posReport);
			em.persist(posProduct);
		}
		posReport.setPosProducts(products);
	}






	@Override
	public void updateQuantityOnDPA(List<PosProductDO> posProducts) {
		for (PosProductDO posProductDO : posProducts) {
			if (posProductDO.getDpaProduct() !=null && posProductDO.getDpaProduct().getId()!=0){
				//the product came form a dpa
				DpaProduct product = em.find(DpaProduct.class, new Integer(posProductDO.getDpaProduct().getId()));
				product.setQuantityReported(posProductDO.getQuantity()+product.getQuantityReported());
				em.persist(product);
			}
		}
		}


	@Override
	public void createOrEditCustomer(CustomerDO customer) {
		if (customer.getId()==0){
			Customer customer2 = dataObjectConverter.convert(customer);
			em.persist(customer2);
			cacheManager.addCustomer(customer);
		}else{
			Customer customer2 = em.find(Customer.class, new Integer(customer.getId()));
			customer2.setAccountancyId(customer.getAccountancyId());
			customer2.setCountry(customer.getCountry());
			customer2.setLocation(customer.getLocation());
			customer2.setName(customer.getName());
			customer2.setState(customer.getState());
			customer2.setZipCode(customer.getZipCode());
			cacheManager.updateCustomer(customer);
			em.persist(customer2);
		}
	}


	@Override
	public List<DpaProductDO> getDPAReportedProducts(Date fromDate, Date toDate) {

        Query query = em.createQuery(HQLQueries.GET_REPORTED_PRDS_FROM_DPAS);
        query.setParameter("fromDate", fromDate);
        query.setParameter("toDate", toDate);
        List results = query.getResultList();
        List<DpaProductDO> dpaProductDOs = new LinkedList<DpaProductDO>();
        for (Object object : results) {
        	DpaProduct prd =(DpaProduct)object;
			DpaProductDO dpaProductDO = dataObjectConverter.convert(prd);
			DpaDO dpaDO = dataObjectConverter.convert(prd.getDpa());
			dpaDO.setCustomer(dataObjectConverter.convert(prd.getDpa().getCustomer()));
			dpaProductDO.setDpa(dpaDO);
			dpaProductDOs.add(dpaProductDO);
		}
		return dpaProductDOs;
	}


	@Override
	public void updatePaidProducts(List<DpaProductDO> products) {
		for (DpaProductDO dpaProductDO : products) {
			DpaProduct prd = em.find(DpaProduct.class, new Integer(dpaProductDO.getId()));
			prd.setQuantityPaid(dpaProductDO.getQuantityPaid());
			
			boolean closed = true;
			for (DpaProduct otPrds : prd.getDpa().getDpaProducts()) {
				if (otPrds.getQuantity()!=otPrds.getQuantityPaid()){
					closed=false;
					break;
				}
			}
			if (closed){
				prd.getDpa().setClosed(true);
				em.persist(prd.getDpa());
			}
		}
		
	}


	@Override
	public  List<DpaDO> getDPADrafts() {
		logger.debug("getting all the dpas from the DB");
		Query q = em.createQuery(HQLQueries.GET_DPA_DRAFTS);
		List<Dpa> l = q.getResultList();

		 List<DpaDO>  dpas = new LinkedList<DpaDO>();
		for (Dpa dpa : l) {
			dpas.add(dataObjectConverter.fullConvert(dpa));
		}
		
		return dpas;
	}


	@Override
	public List<PosDO> getPOSDrafts() {
		logger.debug("getting all the pos from the DB");
		Query q = em.createQuery(HQLQueries.GET_POS_DRAFTS);
		List<Pos> l = q.getResultList();

		 List<PosDO>  poss = new LinkedList<PosDO>();
		for (Pos pos : l) {
			poss.add(dataObjectConverter.fullConvert(pos));
		}
		
		return poss;
	}

	@Override
	public void removeDPAProducts(List<DpaProductDO> removePrds) {
		for (DpaProductDO dpaProductDO : removePrds) {
			DpaProduct prd = em.find(DpaProduct.class, new Integer(dpaProductDO.getId()));
			em.remove(prd);
		}
		
	}


	@Override
	public void removePOSProducts(List<PosProductDO> removePrds) {
		for (PosProductDO pos : removePrds) {
			PosProduct prd = em.find(PosProduct.class, new Integer(pos.getId()));
			em.remove(prd);
		}
		
	}


	@Override
	public List<DpaDO> getClosedAndNotApprovedDPAs() {
		logger.debug("getting all closed and not approved dpas from the DB");
		Query q = em.createQuery(HQLQueries.GET_CLOSED_DPA);
		List<Dpa> l = q.getResultList();

		 List<DpaDO>  dpas = new LinkedList<DpaDO>();
			for (Dpa dpa : l) {
				dpas.add(dataObjectConverter.fullConvert(dpa));
			}
		return dpas;
	}
	@Override
	public Map<Integer, DpaDO> getAllNotClosedDPAs() {
		logger.debug("getting all not closed dpas from the DB");
		Query q = em.createQuery(HQLQueries.GET_NOT_CLOSED_DPA);
		List<Dpa> l = q.getResultList();

		Map<Integer, DpaDO>  dpas = new HashMap<Integer, DpaDO>();
			for (Dpa dpa : l) {
				dpas.put(dpa.getId(),dataObjectConverter.fullConvert(dpa));
			}
		return dpas;
	}


	@Override
	public Map<Integer, DpaDO> getAllDPAs() {
		logger.debug("getting all not closed dpas from the DB");
		Query q = em.createQuery(HQLQueries.GET_ALL_DPAS);
		List<Dpa> l = q.getResultList();

		Map<Integer, DpaDO> dpas = new HashMap<Integer, DpaDO>();
			for (Dpa dpa : l) {
				dpas.put(dpa.getId(),dataObjectConverter.fullConvert(dpa));
			}
		return dpas;
	}








}
